import os


from pptx import Presentation

import shutil
import csv
import json
import traceback
import numpy as np

import time
#import pyautogui

from pptx.enum.shapes import MSO_SHAPE_TYPE
import hashlib

gindex = 0


# 定义一个函数，用于计算文件的哈希值
def calculate_file_hash(file_path, hash_algorithm="sha256", buffer_size=65536):
    """ 获取文件的哈希值 """
    hash_func = hashlib.new(hash_algorithm)  # 使用指定的哈希算法
    with open(file_path, 'rb') as f:
        while chunk := f.read(buffer_size):
            hash_func.update(chunk)  # 更新哈希值
    return hash_func.hexdigest()


def count_shapes(slide,slide_width,slide_height):
    shape_counts = {
        "AUTO_SHAPE": 0,
        "CHART": 0,
        "GROUP": 0,
        "EMBEDDED_OLE_OBJECT": 0,
        "LINE": 0,
        "PICTURE": 0,
        "PLACEHOLDER": 0,
        "TABLE": 0,
        "TEXT_BOX": 0,
        "MEDIA": 0,
        "FREEFORM": 0,
        "LINKED_OLE_OBJECT": 0,
        "NONE": 0,
    }
    has_out_shape = False

    #  for slide in prs.slides:
    for shape in slide.shapes:
            # if is_out_page(shape,slide_width,slide_height):
            #     has_out_shape = True

            try:
                shape_type = shape.shape_type
            except:
                shape_type = 100  #

            if shape_type == 1:
                shape_counts["AUTO_SHAPE"] += 1
            elif shape_type == 3:
                shape_counts["CHART"] += 1
            elif shape_type == 5:
                shape_counts["FREEFORM"] += 1
            elif shape_type == 6:
                shape_counts["GROUP"] += 1
            elif shape_type == 7:
                shape_counts["EMBEDDED_OLE_OBJECT"] += 1
            elif shape_type == 9:
                shape_counts["LINE"] += 1
                #print(shape_type,'---------------------- ',dir(shape))
            elif shape_type == 10:
                shape_counts["LINKED_OLE_OBJECT"] += 1
            elif shape_type == 13:
                width_r = shape.width / slide_width
                height_r = shape.height / slide_height
                if width_r > 0.1 and height_r >0.1 :
                    shape_counts["PICTURE"] += 1
                elif  width_r * height_r > 0.1 * 0.1:
                    shape_counts["PICTURE"] += 1
            elif shape_type == 14:
                shape_counts["PLACEHOLDER"] += 1
                #print(shape_type,'---------------------- ',dir(shape))
            elif shape_type == 16:
                shape_counts["MEDIA"] += 1
            elif shape_type == 17:
                shape_counts["TEXT_BOX"] += 1
            elif shape_type == 19:
                shape_counts["TABLE"] += 1
            else:
                shape_counts["NONE"] += 1
                #print('type: ',shape.shape_type)

    return shape_counts,has_out_shape
def get_text_coord(shape,slide_w_inches,slide_h_inches):
    if shape.left is None or shape.top is None or shape.width is None or shape.height is None:
        return -1,-1,-1,-1
    
    x0 = shape.left.inches/slide_w_inches
    y0 = shape.top.inches/slide_h_inches
    x1 = x0 + shape.width.inches/slide_w_inches
    y1 = y0 + shape.height.inches/slide_h_inches
    return x0, y0, x1, y1

def get_text_from_group(shape):
    """Helper function to get text from a shape, including group shapes."""
    text = ""
    if shape.has_text_frame:
        # print('--------------------group text dir \n',dir(shape))
        if len(shape.text_frame.text.strip()) > 0:
            text_ = shape.text_frame.text
            if (len(text_) > 0):
                text += text_ + "###"

    elif shape.shape_type == 6:  # 6 indicates a group shape
        for sub_shape in shape.shapes:
            text_ = get_text_from_group(sub_shape)
            if(len(text_)>0):
             #print("text_:",text_)
             text += text_
    return text


def get_text_from_slide(slide, slide_w_inches, slide_h_inches):
    """从幻灯片中提取所有文本框的文本和文本框个数"""
    # text_items = []
    textbox_count = 0
    image_count = 0
    TABLEbox_count = 0
    text_coord_list = []
    table_coord_list = []
    table_text_len = 0
    for idx, shape in enumerate(slide.shapes):
        text_items = []
        #print("shape.shape_type:",shape.shape_type)
        # 处理group shape
        group_text = ''
        try:
            if shape.shape_type == MSO_SHAPE_TYPE.GROUP:
                group_text = get_text_from_group(shape)
                group_text = group_text[:-3]
        except:
            continue
        if len(group_text.strip()) > 0:
            text_items.append(group_text)
            textbox_count += 1

            x0, y0, x1, y1 = get_text_coord(shape, slide_w_inches, slide_h_inches)
            text_coord_list.append([x0, y0, x1, y1, group_text])
            # print('shape id ',idx,' group *********************** type ',shape.shape_type,' text_items ',text_items)

            continue
        # 遍历所有图表
        chart_str = ""
        try:
            if shape.shape_type == MSO_SHAPE_TYPE.CHART:

                chart = shape.chart
                chart_location = [shape.top, shape.left]

                # 检查并打印图表标题（如果有的话）
                if chart.has_title and chart.chart_title.text_frame:
                    chart_title = chart.chart_title.text_frame.text
                    chart_str += f"标题：{chart_title}\n"

                # 打印图表的类别轴和值轴标题（如果有的话）
                category_axis_title = chart.category_axis.axis_title.text_frame.text if chart.category_axis.has_title else '无标题'
                value_axis_title = chart.value_axis.axis_title.text_frame.text if chart.value_axis.has_title else '无标题'
                chart_str += f"X轴标题为{category_axis_title}，"
                chart_str += f"Y轴标题为{value_axis_title}\n"

                # 遍历图表中的所有系列
                for series in chart.series:
                    chart_str += f"曲线名为{series.name}\n"
                    if chart.chart_type in [XL_CHART_TYPE.XY_SCATTER, XL_CHART_TYPE.BUBBLE]:
                        # 对于散点图和气泡图，打印 x 和 y 值
                        chart_str += f"X轴的值为: {series.x_values}，"
                        chart_str += f"Y轴的值为: {series.y_values}\n"
                    else:
                        # 对于其他类型的图表，打印值和类别
                        chart_str += f"类别为{series.categories}，值为{series.values}\n"

            # print("chart_str:", chart_str)
        except:
            continue
        if len(chart_str.strip()) > 0:
            text_items.append(chart_str)
            textbox_count += 1

            x0, y0, x1, y1 = get_text_coord(shape, slide_w_inches, slide_h_inches)
            text_coord_list.append([x0, y0, x1, y1, chart_str])
            # print('shape id ',idx,' group *********************** type ',shape.shape_type,' text_items ',text_items)

            continue

        table_str = ""
        text_valid = 0
        try:
            # 遍历所有表格
            if shape.shape_type == MSO_SHAPE_TYPE.TABLE:
                # 处理表格
                table = shape.table
                table_location = [shape.top, shape.left]

                # 获取表格的行数和列数
                rows = len(table.rows)
                cols = len(table.rows[0].cells) if rows > 0 else 0
                table_infor = {}

                text_row_ns = []
                # 遍历表格的每一行
                for row_index in range(rows):
                    # 遍历每一列
                    text_row_n = 0
                    for col_index in range(cols):
                        # 获取当前单元格
                        cell = table.cell(row_index, col_index)
                        is_merge_origin = cell.is_merge_origin
                        is_spanned = cell.is_spanned
                        merge = cell.merge
                        split = cell.split
                        width = cell.span_width
                        height = cell.span_height
                        text = cell.text.replace('\n', '   ').strip()
                        text_row_n += len(text)
                        if width > 1:
                            for _ in range(0, width):
                                table_infor[tuple([row_index, col_index + _])] = '| ' + text + ' '
                        if height > 1:
                            for _ in range(0, height):
                                table_infor[tuple([row_index + _, col_index])] = '| ' + text + ' '
                        if width == 1 and height == 1 and tuple([row_index, col_index]) not in table_infor:
                            table_infor[tuple([row_index, col_index])] = '| ' + text + ' '
                    text_row_ns.append(text_row_n)
                for row_index in range(0, rows):
                    row_info = []
                    if (text_row_ns[row_index] == 0):
                        continue
                    text_valid += text_row_ns[row_index]
                    for col_index in range(0, cols):
                        row_info.append(table_infor[tuple([row_index, col_index])])
                    table_str += ''.join(row_info) + ' |\n'
                    if row_index == 0:
                        row_info = []
                        for col_index in range(0, cols):
                            row_info.append('| ---')
                        table_str += ''.join(row_info) + ' |\n'
                #print("len(table_str.strip()):",len(table_str.strip()))
        except:
            continue
        
        if len(table_str.strip()) > 20 and len(table_str.strip()) < 7000 and text_valid > 10:
            TABLEbox_count += 1
            text_items.append(table_str)
            textbox_count += 1
            table_text_len += len(table_str.strip())
            x0, y0, x1, y1 = get_text_coord(shape, slide_w_inches, slide_h_inches)
            #print("len(table_str.strip())：",len(table_str.strip()))
            if len(table_str.strip()) > 500:
              table_coord_list.append([x0, y0, x1, y1, table_str])
            else:
              text_coord_list.append([x0, y0, x1, y1, table_str])
            # print('shape id ',idx,' group *********************** type ',shape.shape_type,' text_items ',text_items)

            continue

        image_text = ''

        if len(image_text.strip()) > 10:
            # print("image_text:", image_text)
            image_count += 1
            text_items.append(image_text)
            x0, y0, x1, y1 = get_text_coord(shape, slide_w_inches, slide_h_inches)
            text_coord_list.append([x0, y0, x1, y1, image_text])
            # print('shape id ',idx,' group *********************** type ',shape.shape_type,' text_items ',text_items)

            continue

        if not shape.has_text_frame:
            continue
        textbox_count += 1

        if shape.text_frame.text is not None:
            if len(shape.text_frame.text.strip()) > 0:
                text_items.append(shape.text_frame.text)

        if len(text_items) > 0:
            x0, y0, x1, y1 = get_text_coord(shape, slide_w_inches, slide_h_inches)
            joined_text = ' '.join(text_items)
            text_coord_list.append([x0, y0, x1, y1, joined_text])

    # print(' joined_text ',text_coord_list)
    # print(' text_coord_list ',text_coord_list)
    # return textbox_count, "\n".join(text_items),text_coord_list
    #print("table_text_len:",table_text_len )
    if table_text_len < 7000: # table_coord_list  保存表格长度大于2000 ，小于10000的表格，如果表格总长度不超过10000，就保存到text_coord_list 中
      text_coord_list = text_coord_list + table_coord_list
      
    text_coord_list.sort(key=lambda x: (x[1], x[0]))
    return image_count, TABLEbox_count, textbox_count, text_coord_list

def check_file_extension_os_path(file_path):
    # 获取文件的后缀名
    _, ext = os.path.splitext(file_path)
    # 检查后缀名是否为.ppt（不区分大小写）
    return ext.lower() == '.ppt'

def get_page_info(ppt_path):
    slide_data = []
    try:
      presentation = Presentation(ppt_path)
    except:
      return slide_data

    slide_width = presentation.slide_width
    slide_height = presentation.slide_height

    slide_w_inches = presentation.slide_width.inches
    slide_h_inches = presentation.slide_height.inches


    idx = 0
    for index, slide in enumerate(presentation.slides):
            #print("index:",index+1)
            image_count,TABLEbox_count,textbox_count, slide_text = get_text_from_slide(slide, slide_w_inches, slide_h_inches)
            shape_counts, has_out_shape = count_shapes(slide, slide_width, slide_height)
            hidden_slides = 1
            if slide._element.get("show") == "0":
                hidden_slides = 0
            shape_counts["slide_number"] = index + 1
            shape_counts["TEXT"] = slide_text
            shape_counts["TEXT_BOX"] = textbox_count
            shape_counts["image_BOX"] = image_count
            shape_counts["OUT"] = int(has_out_shape)
            shape_counts["hidden_slides"] = hidden_slides
            slide_data.append(shape_counts)

    return slide_data


def create_directory_for_file(file_path):
    file_dir_path = os.path.dirname(file_path)
    if not os.path.exists(file_dir_path):
        os.makedirs(file_dir_path,exist_ok=True)

def write_category_to_csv(csv_data):
    write_to_csv(csv_data, 'ppt_path', 0, 0)
    write_to_csv(csv_data, 'slide_number', 0, 1)
    write_to_csv(csv_data, 'TEXT_BOX', 0, 2)
    write_to_csv(csv_data, 'TABLE', 0, 3)
    write_to_csv(csv_data, 'PICTURE', 0, 4)
    write_to_csv(csv_data, 'CHART', 0, 5)
    write_to_csv(csv_data, 'TEXT', 0, 6)
    write_to_csv(csv_data, 'TEXT_LEN', 0, 7)
    write_to_csv(csv_data, 'CN_TEXT_LEN', 0, 8)
    write_to_csv(csv_data, 'EN_TEXT_LEN', 0, 9)
    write_to_csv(csv_data, 'AUTO_SHAPE', 0, 10)
    write_to_csv(csv_data, 'GROUP', 0, 11)
    write_to_csv(csv_data, 'LINE', 0, 12)
    write_to_csv(csv_data, 'PLACEHOLDER', 0, 13)
    write_to_csv(csv_data, 'MEDIA', 0, 14)
    write_to_csv(csv_data, 'FREEFORM', 0, 15)
    write_to_csv(csv_data, 'LINKED_OLE_OBJECT', 0, 16)
    write_to_csv(csv_data, 'EMBEDDED_OLE_OBJECT', 0, 17)
    write_to_csv(csv_data, 'NONE', 0, 18)
    write_to_csv(csv_data, 'OUT', 0, 19)
    write_to_csv(csv_data, 'animation', 0, 20)
    write_to_csv(csv_data, 'GLOBAL_PATH', 0, 21)
    write_to_csv(csv_data, 'PNG_PATH', 0, 22)
    write_to_csv(csv_data, 'complexity', 0, 23)
    write_to_csv(csv_data, 'hidden_slides', 0, 24)

def get_csv(csv_path):

    with open(csv_path, 'a+') as cfile:
        reader = csv.reader(cfile)
        csv_data = list(reader)
    write_category_to_csv(csv_data)
    return csv_data

def write_to_csv(csv_data, data, row, col):
    #row += 1
    #col += 1
    # 确保行数足够
    while len(csv_data) < row+1:
        csv_data.append([])

    # 确保列数足够
    for r in csv_data:
        while len(r) < col+1:
            r.append('')
    
    csv_data[row][col] = data
def write_json(json_path, json_data):
    with open(json_path, 'w', encoding='utf-8') as file:
        file.write(json.dumps(json_data, ensure_ascii=False))
def is_english_alpha(char):
    return 'a' <= char.lower() <= 'z'
def count_chinese_and_english_characters(str_list):
    s = ''
    for strs in str_list:
        if len(strs)>0:
            s += strs[4]
    str_len = len(s)
    count_chinese = 0
    count_english = 0
    for char in s:
        try:
            if 'CJK UNIFIED' in unicodedata.name(char):
                count_chinese += 1
                #print('Chinese:       ',char)
        except:    
            a = 0
        if is_english_alpha(char):
            #print('English:       ',char)
            count_english += 1
    return str_len,count_chinese, count_english

def img_gpt4(ocrstr, prompt_path):
    # headers = {
    #   "Content-Type": "application/json",
    #   "Authorization": f"Bearer {api_key}"
    # }

    # promptstr = "请详细用中文讲解并提取图片中的以下内容：1.标题。2.关键词（用逗号分隔）。3.结论。4.图片中表格、示意图和逻辑树的描述。5.整体详细描述。你的讲解需要科学严谨，确保信息完整、清晰、准确，并对每个部分进行详细说明，涵盖所有要求的信息。以下是图片中的部分文本信息，每句文本之间使用###分隔，希望这些文本能帮你理解和避免错别字：" + ocrstr
    promptstr = "请详细用中文讲解并提取图片中的以下内容：1.标题。2.结论。3.图片中表格、示意图和逻辑树的描述。4.整体详细描述。你的讲解需要科学严谨，确保信息完整、清晰、准确，并对每个部分进行详细说明，涵盖所有要求的信息。以下是图片中的部分文本信息，每句文本之间使用###分隔，希望这些文本能帮你理解和避免错别字：" + ocrstr

    # print('promptstr: ',promptstr)
    with open(prompt_path, 'w', encoding='UTF-8') as f:
        json.dump(promptstr, f, ensure_ascii=False, indent=4)

def img_gpt4_prompt(ocrstr):
    # headers = {
    #   "Content-Type": "application/json",
    #   "Authorization": f"Bearer {api_key}"
    # }

    # promptstr = "请详细用中文讲解并提取图片中的以下内容：1.标题。2.关键词（用逗号分隔）。3.结论。4.图片中表格、示意图和逻辑树的描述。5.整体详细描述。你的讲解需要科学严谨，确保信息完整、清晰、准确，并对每个部分进行详细说明，涵盖所有要求的信息。以下是图片中的部分文本信息，每句文本之间使用###分隔，希望这些文本能帮你理解和避免错别字：" + ocrstr
    promptstr = "请详细用中文讲解并提取图片中的以下内容：1.标题。2.结论。3.图片中表格、示意图和逻辑树的描述。4.整体详细描述。你的讲解需要科学严谨，确保信息完整、清晰、准确，并对每个部分进行详细说明，涵盖所有要求的信息。以下是图片中的部分文本信息，每句文本之间使用###分隔，希望这些文本能帮你理解和避免错别字：" + ocrstr

    return promptstr

def imgocr2gpt4(data,prompt_path):
    texts = [item[-1] for item in data]  # 只取文本，不用坐标
    # Use a dictionary to remove duplicates while preserving order
    # 去掉相同的文本
    seen = {}
    unique_data = []
    for item in texts:
      item = item.lstrip()
      if item not in seen:
        seen[item] = True
        unique_data.append(item)

   # Join the unique elements with "###"
    orc = "###".join(unique_data)

# orc = "###".join(texts)
    resstr = img_gpt4(orc, prompt_path)

def imgocr2gpt4_prompt(data):
    texts = [item[-1] for item in data]  # 只取文本，不用坐标
    # Use a dictionary to remove duplicates while preserving order
    # 去掉相同的文本
    seen = {}
    unique_data = []
    for item in texts:
      item = item.lstrip()
      if item not in seen:
        seen[item] = True
        unique_data.append(item)

   # Join the unique elements with "###"
    orc = "###".join(unique_data)

# orc = "###".join(texts)
    return img_gpt4_prompt(orc)

def is_valid_pptx(filename):
    valid_extensions = {'.pptx', '.pptxm'}
    return filename.lower().endswith(tuple(valid_extensions))

def pptx2prompt(pptx_path,log_file,data_prompt_infos):
    # if is_valid_pptx(pptx_path) == False:
    #     with open(log_file, 'a', encoding='utf-8') as f:
    #         f.write(f"Error processing file: {ppt_path}\n")
    #     return False
    try:
      slides_info = get_page_info(pptx_path)

      for idx, slide in enumerate(slides_info):
          page_count = idx + 1
          prompt = imgocr2gpt4_prompt(slide['TEXT'] )
          #print("prompt:",prompt)
          data_prompt_infos.append({
                                 "image_page": page_count,
                                 "prompt":prompt
                                 })


    except Exception as e:
         # with open(log_file, 'a', encoding='utf-8') as f:
         #     f.write(f"Error processing file: {}\n")
         return False

    return True

# filepath = r"E:\share3\ppt处理服务部署\uploads\2.pptx"
# save_path = r"E:\share3\ppt处理服务部署\data"
# if not os.path.exists(save_path):
#             os.makedirs(save_path, exist_ok=True)
# log_file = "error.log"
# data_prompt_infos = []
# pptx2prompt(filepath, save_path, log_file, data_prompt_infos)
# print(data_prompt_infos)
